<?php
	/**
	* @auther Bin
	* @class RainStorm implements some same interfaces of jQuery
	* @class Hurricane base selector engine
	* @class PSS/PSSScanner just like css, using selector to replace data in elements
	* @version 0.04 beta
	*/

	/*
	  @changelog
		
		0.04
			为PSS的html输出方法加了一个参数，当使用true，且导入的为本地模板时，会自动修正所有本地资源的路径（相对于基础模板而言）
			例如
				$pss = PSS(a,b);
				$pss->html(true);
				所有本地路径以a的路径为相对
				
		0.03
			修正一些bug，修正UTF8的乱码问题
			现在模板文件中可以直接写php代码，也可以使用PSS进行外部注入
			
		0.02
			修正一些bug
			
		0.01
			实现了一些PSS基本功能
			RainStorm可以独立成dom操作库
			Hurricane可以独立成dom选择器

	*/

	/*
		入口函数
	*/
	function PSS($url,$pssFile){
		return new PSSScanner($url,$pssFile);
	}

		
	class RainStorm{
		var $hurricanes;
		var $length;

		static function loadFromHtml($html){
			$dom = new DomDocument();
			$html = RainStorm::getSafeHtml($html);
			@$dom->loadHTML($html);
			return new RainStorm($dom);
		}

		static function loadFromFile($url){
			$dom = new DomDocument('1.0','UTF-8');
			$html = RainStorm::getSafeHtml(file_get_contents($url));
			@$dom->loadHTML($html);
			return new RainStorm($dom);
		}

		/*
			从PHP文件中调用文本，会执行PHP的内容（效率低下）
		*/
		static function loadFromPHP($url){
			$dom = new DomDocument();
			ob_start();
			include($url);
			$html = ob_get_contents();
			ob_end_clean();
			$html = RainStorm::getSafeHtml($html);
			@$dom->loadHTML($html);
			return new RainStorm($dom);
		}

		static function getSafeHtml($html){
			//如果是GBK编码
			if(mb_check_encoding($html,"GBK")){
				$html = preg_replace("/(<meta[^>]*)(gbk|gb2312)/i","$1utf8",$html);
				$html = mb_convert_encoding($html,'UTF-8','GBK');
			}
			$html = '<?xml version="1.0" encoding="utf8"?>'. $html;
			return $html;
		}

		function __construct($args = null){
			if(is_array($args)){
				$this->hurricanes = $args;
			}else{
				$this->hurricanes = array();
				if(is_string($args)){
					$this->hurricanes[] = Hurricane::loadFromHtml($args);
				}
				elseif(get_class($args) == 'Hurricane'){
					$this->hurricanes[] = $args;
				}
				else{
					$this->hurricanes[] = new Hurricane($args);
				}
			}
			$this->length = count($this->hurricanes);
			return $this;
		}

		/*
			由于DomDocument到5.3.6才支持取出innerHtml，所以如果要取的话得用特殊的方法，但我们只需要赋值，没必要查看内容
			已解决该bug
		*/
		function html($html = null){
			if(is_null($html)){
				$html = array();
				foreach($this->hurricanes as $hurricane){
					$_html = '';
					$this->buildHtml($hurricane->current,$_html,true);
					$html[] = $_html;
				}
				return implode($html);
			}else{
				$this->clear();
				foreach($this->hurricanes as $hurricane){
					$frag = $hurricane->current->ownerDocument->createDocumentFragment();
					$frag->appendXML($html);
					$hurricane->current->appendChild($frag);
				}
			}
		}

		/*
			只替换下一级的文本节点，不对元素造成影响
		*/
		function text($text = null){
			if(is_null($text)){
				//这个一般不会用到，所以只return出来nodeValue
				$text = array();
				foreach($this->hurricanes as $hurricane){
					$text[] = $hurricane->current->nodeValue;
				}
				return implode($text);
			}else{
				foreach($this->hurricanes as $hurricane){
					$current = $hurricane->current;
					$flag = 0;
					for($i=0;$i<$current->childNodes->length;$i++){
						if($current->childNodes->item($i)->nodeType == 3){
							if(preg_match('/^[\s\t\n\r]+$/',$current->childNodes->item($i)->nodeValue)){
								continue;
							}else{
								$current->childNodes->item($i)->nodeValue = $text;
								$flag++;
								break;
							}
						}
					}
					if(!$flag){
						$textNode = $current->ownerDocument->createTextNode($text);
						$current->appendChild($textNode);
					}
				}
			}
		}

		function clear(){
			foreach($this->hurricanes as $hurricane){
				$node = $hurricane->current;
				while($node->childNodes->length){
					$node->removeChild($node->firstChild);
					
				}
			}
			return $this;
		}

		static $noChildren = array('input','img','link','meta');
		private function buildHtml($node,&$html,$isInner = false){
			if($node->nodeType == 1){
				//当不取outerHTML的时候，第一次不拼装父元素
				if(!$isInner){
					$html .= '<' . $node->tagName;
					//填充属性
					if($node->hasAttributes()){
						//var_dump($node->attributes);
						foreach($node->attributes as $attr){
							$html .= " {$attr->nodeName}=\"{$attr->nodeValue}\"";
						}
						
					}
				}
				if(in_array($node->tagName,RainStorm::$noChildren)){
					$html .= '/>';
				}
				else{
					$html .= '>';
				}
				if($node->hasChildNodes()){
					foreach($node->childNodes as $child){
						$this->buildHtml($child,$html);
					}
				}
				if(!$isInner){
					$html .= '</' . $node->tagName . '>';
				}
				
			}
			elseif($node->nodeType == 3){
				$html .= $node->nodeValue;
			}
			
		}

		function eq($index){
			return isset($this->hurricanes[$index]) ? new RainStorm($this->hurricanes[$index]) : new RainStorm();
		}

		function attr($attrName,$attrValue = null){
			if(is_array($attrName)){
				foreach($this->hurricanes as $hurricane){
					foreach($attrName as $name => $val){
						$hurricane->current->setAttribute($name,$val);
					}
				}
			}
			elseif(is_string($attrName)){
				$flag = 0;
				if(is_null($attrValue)){
					$ret = array();
					$flag = 1;
				}
				foreach($this->hurricanes as $hurricane){
					if($flag){
						$val = $hurricane->current->getAttribute($attrName) or $val = '';
						$ret[] = $val;
					}else{
						$hurricane->current->setAttribute($attrName,$attrValue);
					}
				}
			}
		}

		function match($selector){
			$elem = $this->firstElement();
			$hurricane = new Hurricane($elem->ownerDocument,$elem);
			$rule = $hurricane->anasylzeSelector($selector);
			return $hurricane->matchRule($rule);
		}
		
		function parentNode(){
			$elem = $this->firstElement();
			return new RainStorm($elem);
		}
		/* 一时半会用不上，回头再补
		function parents($selector = null){
			//$elem = $this->firstElement();
			//while()
			foreach($this->hurricanes as $hurricane){
				if(is_null($selector)){
					
				}
			}
		}
		*/
		
		function append($arg){
			$elem = $this->firstElement();
			if(is_string($arg)){
				$frogment = $this->firstElement()->ownerDocument->createDocumentFragment();
				$frogment->appendXML($arg);
				$elem->appendChild($frogment);
			}
			else{
				$clz = get_class($arg);
				if($clz === 'RainStorm'){
					foreach($arg->hurricanes as $hurricane){
						$elem->appendChild($hurricane->current);
					}
				}
				elseif($clz == 'Dom'){
					//暂时用不到，以后再补充
				}
			}
			return $this;
		}

		function remove(){
			foreach($this->hurricanes as $hurricane){
				@$hurricane->current->parentNode->removeChild($hurricane->current);
			}
		}

		function cloneNode(){
			$ret = array();
			foreach($this->hurricanes as $hurricane){
				$ret[] = new Hurricane($hurricane->current->cloneNode(true));
			}
			return new RainStorm($ret);
		}

		function firstElement(){
			foreach($this->hurricanes as $hurricane){
				return $hurricane->current;
			}
		}
		/*
		function css($cssName,$cssValue){
			if(is_array($attrName)){
				foreach($this->hurricanes as $hurricane){
					foreach($attrName as $name => $val){
						$hurricane->current->setAttribute($name,$val);
					}
				}
			}
			elseif(is_string($attrName)){
				$flag = 0;
				if(is_null($attrValue)){
					$ret = array();
					$flag = 1;
				}
				foreach($this->hurricanes as $hurricane){
					if($flag){
						$val = $hurricane->current->getAttribute($attrName) or $val = '';
						$ret[] = $val;
					}else{
						$hurricane->current->setAttribute($attrName,$attrValue);
					}
				}
			}
		}
		*/

		function find($selector){
			$hurrcanes = array();
			foreach($this->hurricanes as $hurricane){
				$matchedElements = $hurricane->find($selector);
				foreach($matchedElements as $element){
					$hurrcanes[] = new Hurricane($element);
				}
			}
			return new RainStorm($hurrcanes);
		}
	}
	
	class Hurricane{

		/**************************************

		*********************************************************
		**在和RainStorm配合的情况下，用不到这些方法，如果独立出去才用得到**
		*********************************************************
		static function loadFromHtml($html){
			$dom = new DomDocument();
			@$dom->loadHTML($html);
			return new Hurricane($dom);
		}

		static function loadFrom($url){
			$dom = new DomDocument();
			$html = file_get_contents($url);
			@$dom->loadHTML($html);
			echo $dom->saveHTML();
			return new Hurricane($dom);
		}
		****************************************/

		var $current;

		function __construct(&$dom,$instance = null){
			if(!is_null($instance)){
				$this->current = $instance;
			}else{
				$this->current = $dom;
			}
		}

		public function anasylzeSelector(&$selector){
			$stack = array();
			$mode = 0;
			$ret = array();
			$temp = array('attr' => array(),'tag' => '');
			//补一个空格方便处理最后的项目
			$selector .= ' ';
			for($i=0,$len=strlen($selector);$i<$len;$i++){
				$char = $selector[$i];

				//id或class都归为属性，但是ID要特殊处理
				if($char == '.' || $char == '#'){
					$token = array();
					while(++$i<$len){
						
						if($this->isChar($selector[$i])){
							$token[] = $selector[$i];
						}else{
							$i--;
							break;
						}
					}
					$token = implode($token);
					if($char == '.'){
						$temp['attr'][] = array('class' => $token);
					}else{
						$temp['id'] = $token;
						if(count($ret)){
							unset($ret);
							$ret = array();
						}
						//找到最近的空格
						while(!preg_match("/\t|\s/",$selector[$i]) && $i < $len){$i++;}
						$i--;
					}
					unset($token);
				}
				elseif($char == '['){
					$token = array();
					$attrName = null;
					$attrValue = null;
					while(++$i < $len){
						if($selector[$i] == '='){
							$attrName = implode($token);
							unset($token);
							$token = array();

							//从这里开始读取属性
							$next = $selector[++$i];
							if($next == '"' || $next == "'"){
								$limit = $next;
								$i++;
							}else{
								$limit = ']';
							}
							while($selector[$i] != $limit && $i < $len){
								$token[] = $selector[$i];
								$i++;
							}
							$attrValue = implode($token);
						}
						if($selector[$i] == ']'){
							break;
						}
						if($this->isChar($selector[$i])){
							$token[] = $selector[$i];
						}
					}
					if(count($token) && is_null($attrName)){
						$attrName = implode($token);
					}
					$temp['attr'][] = array($attrName => $attrValue);
					unset($token);
				}
				elseif($char === ':'){
					$token = array();
					while(++$i < $len){
						if($this->isChar($selector[$i])){
							$token[] = $selector[$i];
						}
						else{
							if(!isset($temp['faker'])){
								$temp['faker'] = array();
							}
							$temp['faker'][] = array('class' => implode($token));
							//如果存在参数，读取参数
							if($selector[$i] === '('){
								unset($token);
								$token = array();
								while(++$i < $len){
									if($selector[$i] === ')'){
										$temp['faker'][count($temp['faker']) - 1]['args'] = trim(implode($token));
										$i--;
										break;
									}
									$token[] = $selector[$i];
								}
							}
							$i--;
							break;
						}

					}
					//$fakerClass = ''
					unset($token);
				}
				elseif(preg_match("/\s|\t/",$char)){
					if(strlen($temp['tag']) || count($temp['attr']) || isset($temp['id'])){
						$ret[] = $temp;
						$temp = array('tag' => '','attr' => array());
					}
				}
				else{
					//防止报错
					if(!$this->isChar($selector[$i])){
						continue;
					}
					do{
						if($this->isChar($selector[$i])){
							$temp['tag'] .= $selector[$i];
						}else{
							$i--;
							break;
						}
					}while(++$i < $len);
				}

			}
			return $ret;
		}
		private function isChar($c){
			return preg_match('/[\w-]/',$c);
		}

		public function matchRule(&$element,$rules){
			//如果有ID，优先判断id
			if(isset($rules['id'])){
				if($element->getAttribute('id') !== $rules['id']){
					return false;
				}else{
					//ID只要匹配，无论何时都return true
					return true;
				}
			}
			//如果有tag，优先判断tag
			if(!empty($rules['tag'])){
				if($element->tagName !== $rules['tag']){
					return false;
				}
			}
			if(isset($rules['attr'])){
				foreach($rules['attr'] as $rule){
					foreach($rule as $name => $value){
						$attrNode = $element->getAttributeNode($name);
						//class的情况只判断是否存在即可
						if($name == 'class'){
							if($attrNode && preg_match("/^$value$|^$value\s+|\s+$value$/",$attrNode->nodeValue)){
								continue;
							}
						}else{
							if($attrNode && $attrNode->nodeValue == $value){
								continue;
							}
						}
						return false;
					}
				}
			}
			//如果有伪类
			/*
				暂时无力处理伪类
				伪类已拆分，但没有处理选择
			*/
			
			return true;
		}

		private function filter(&$elements,$rules,$nodes = null){
			//$nodes不为空的情况只有第一次，其余情况在给定集合中筛选父元素是否符合条件
			if(!is_null($nodes)){
				foreach($nodes as $node){
					$elements[] = $node;
				}
				foreach($elements as $key => $element){
					if(!$this->matchRule($element,$rules)){
						unset($elements[$key]);
					}
				}
			}
			//其余的情况，遍历父元素进行筛选
			else{
				foreach($elements as $key => $element){
					$doRemove = true;
					$current = $element;
					while($current = $current->parentNode){
						if(!$current || $current->tagName == 'html'){
							break;
						}
						//父元素和选取相反 只要有一个匹配上了，就可以通过
						if($this->matchRule($current,$rules)){
							$doRemove = false;
							break;
						}	
					}
					if($doRemove){
						unset($elements[$key]);
					}
				}
				
			}
			//去除数组中空的元素，将来这些可以用链表代替
			$temp = array();
			foreach($elements as $key => $element){
				if(!is_null($element)){
					$temp[] = $element;
				}
			}
		}
		
		private function findSingle(&$selector){
			$selGroup = $this->anasylzeSelector($selector);
			//只有第一次是真正查找的，其他情况下只是进行匹配
			$isSearch = true;
			$elements = array();
			for($i=count($selGroup)-1;$i>-1;$i--){
				$item = $selGroup[$i];
				if($isSearch){
					$isSearch = false;
					//如果存在id
					if(isset($item['id'])){
						$element = $this->current->getElementById($item['id']);
						//如果该元素不存在，则不再进行匹配
						if($element){
							$elements[] = $element;
						}else{
							return null;
						}
					}else{
						$nodes = $this->current->getElementsByTagName(!empty($selGroup['tag']) ? $selGroup['tag'] : '*');
						$this->filter($elements,$selGroup[$i],$nodes);
					}
				}else{
					$this->filter($elements,$selGroup[$i]);
				}
				
			}
			return $elements;
		}

		/*
			暴露在外的查找方法
		*/
		function find($selector){
			$each = explode(',',$selector);
			$matchedElement = array();
			$singleBox = array();
			foreach($each as $e){
				$temp = $this->findSingle($e);
				if(!is_null($temp)){
					foreach($temp as $ele){
						$path = $ele->getNodePath();
						if(!isset($singleBox[$path])){
							$singleBox[$path] = 1;
							$matchedElement[] = $ele;
						}
					}
				}
				
			}
			return $matchedElement;
		}
		
	}

	function _pssCallback($a){
		$content = $a[1];
		$pattern = "/(.+)\{|([\s\t]*([^\s]+)[\s\t]*:[\s\t]*([^\:\n]+)[\s\t]*)*|(\})/m";
		preg_match_all($pattern,$content,$arr);
		//print_r($arr);
		if(!isset($GLOBALS['_psstemp']['constant'])){
			$GLOBALS['_psstemp']['constant'] = array();
		}
		foreach($arr[0] as $key => $val){
			if(!empty($arr[1][$key])){
				$temp = array('items' => array());
				$temp['selector'] = $arr[1][$key];
			}
			elseif(!empty($arr[3][$key])){
				$temp['items'][$arr[3][$key]] = $arr[4][$key];
			}
			elseif(!empty($arr[5][$key])){
				$GLOBALS['_psstemp']['constant'][] = $temp;
			}
		}
		return '';
	}

	function _pssCallback2($a){
		$data = $GLOBALS['_psstempdata'];
		$temp = explode('.',$a[0]);
		$d = $data;
		for($i=1;$i<count($temp);$i++){
			@$d = $data[$temp[$i]];
		}
		$value = str_replace($GLOBALS['_psstempvalue'],$a[0],$d);
		$GLOBALS['_psstempvalue'] = $value;
		return '';
	}
	
	class PSSScanner{
		var $content;
		var $filePath;
		var $instance;
		static $FUNC_COUNT = 0;
		
		function __construct($url,$path){
			$this->filePath = $url;
			if(is_array($path)){
				$c = array();
				foreach($path as $key => $val){
					$c[] = file_get_contents($val);
				}
				$this->content = implode("\n\r",$c);
			}else{
				$this->content = file_get_contents($path);
			}
			//去除注释
			$this->content = preg_replace(array("/\/\*[\s\S]+\*\//",'/\/\/[^\n]+/m'),"",$this->content);
			$rules = $this->anasylze();
			if(isset($rules['constant'])){
				$constant = $rules['constant'];
				unset($rules['constant']);
			}else{
				$constant = null;
			}
			$code = $this->translate($rules,$constant);
			eval($code);
			$this->instance = $GLOBALS['_pssInstance'];
			unset($GLOBALS['_pssInstance']);
		}

		function anasylze(){
			//得到被声明的常量
			$this->content = preg_replace_callback("/#constant([\s\S]+)#cend/",'_pssCallback',$this->content);
			if(isset($GLOBALS['_psstemp'])){
				$ret = $GLOBALS['_psstemp'];
				unset($GLOBALS['_psstemp']);
			}else{
				$ret = array();
			}
			
			/*
			这里实在是没有精力了，将来再优化吧
			$pattern = "/\{|\}|(set)\s*{|[\t\s]*([^\n\:\{\}]+)[\t\s]*|:/";
			preg_match_all($pattern,$this->content,$arr);
			$count = null;
			foreach($arr[0] as $key => $val){
				//if()
				if($arr[1][$key] == 'set'){
					//$count = 
				}
				if($count === 0){
					break;
				}
			}
			print_r($arr);
			echo $arr[0][4][0] == "\r" ? 1 : 0;
			exit;

			*/
			mb_ereg_search_init($this->content,"\{|\}|(set)\s*{");
			$setPos = array();
			$len = strlen($this->content);
			$i = 0;
			$count = 0;
			$temp = array('set' => '','body' => '');
			$setIndex = null;
			$setCount = null;
			$funcIndex = null;
			$isConstant = false;
			while($pos = mb_ereg_search_pos()){
				$matches = mb_ereg_search_getregs();
				if($matches[1] === 'set'){
					$setIndex = $pos[0] + $pos[1];
					$setCount = $count;
					if(!is_null($funcIndex)){
						$temp['body'] .= substr($this->content,$funcIndex,$pos[0] - $funcIndex);
					}
					$count++;
				}
				elseif($matches[0] == '{'){
					if(!$count){
						$temp['selector'] = trim(substr($this->content,$i,$pos[0] - $i));
						$funcIndex = $pos[0] + $pos[1];
					}
					$count++;
				}
				else{
					$count--;
					if(!is_null($setIndex)){
						if($count == $setCount){
							$temp['set'] .= substr($this->content,$setIndex,$pos[0] - $setIndex);
							$setIndex = null;
							$setCount = null;
							$funcIndex = $pos[0] + $pos[1];
						}
					}
					elseif($count == 0){
						$temp['body'] .= trim(substr($this->content,$funcIndex,$pos[0] - $funcIndex));
						$ret[] = $temp;
						unset($temp);
						$temp = array('set'=>'','body'=>'');
						$funcIndex = null;
					}
					
				}
				$i = $pos[0] + $pos[1];
			}
			return $ret;
		}

		function translate(&$rules,$constant = null){
			$code = array();
			//print_r($rules);
			foreach($rules as $rkey => $rule){
				if(isset($rule['set'])){
					$str = $rule['set'];
					/*
						1 define
						2 define x
						3 define x y
						4 selector
						5 attrName
						6 attrValue
					*/
					preg_match_all("/(#define)[\s\t]+(\S+)[\s\t]+(\S+)|(.+)\{|([^\n\r]+):([^\n\r]+)/m",$str,$arr);
					$temp = array(
						'define' => array(),
						'items' => array()
					);
					foreach($arr[0] as $key => $item){
						if(!empty($arr[1][$key])){
							$temp['define'][$arr[2][$key]] = $arr[3][$key];
						}
						elseif(!empty($arr[4][$key])){
							$temp['items'][] = array('selector' => trim($arr[4][$key]));
						}
						elseif(!empty($arr[6][$key])){
							$temp['items'][count($temp['items']) - 1][trim($arr[5][$key])] = trim($arr[6][$key]);
						}
					}
				}
				$rules[$rkey]['body'] = preg_replace("/return([^\;]*);.*$/",'$_pssInstance->setData('."$1".');',$rules[$rkey]['body']);
				$rules[$rkey]['body'] = '$_pssInstance->setSelector(\'' . $rule['selector'] .'\');' . "\n\r" . $rules[$rkey]['body'];
				$rules[$rkey]['body'] .= "\n\r" . '$_pssInstance->setRules(' . var_export($temp,true) . ');' . "\n\r";
				$code[] = $rules[$rkey]['body'];
			}
			if(!is_null($constant)){
				$code[] = '$_pssInstance->setConstant(' . var_export($constant,true) . ');';
			}
			//$code = 
			return '$GLOBALS["_pssInstance"] = $_pssInstance = new PSS("'.$this->filePath.'");' . "\n\r" . implode("\n\r",$code) . "\n\r" . '$_pssInstance->execute();';
		}

		/*
			注：saveResource会自动修正本地文件的路径，为实验性功能，慎用！
		*/
		function html($saveRerouce = false){
			$html = $this->instance->storm->hurricanes[0]->current->saveHTML();
			if($saveRerouce){
				$base = $this->filePath;
				if(strpos($base,'http://') !== false){
					//准备文件容器
				}else{
					$baseDir = dirname($base);
					$GLOBALS['_psstempbase'] = $baseDir;
				}
				
				$html = preg_replace_callback("/(<link.*?href=)('((?!http:\/\/)[^\']+)'|\"((?!http:\/\/)[^\"]+)\")|(<(?:img|script).*?src=)('((?!http:\/\/)[^\']+)'|\"((?!http:\/\/)[^\"]+)\")/","_pssCallback3",$html);
				$html = preg_replace_callback("/(background.*?:.*?url\()\s*((?!http:\/\/).+)\s*(\))/","_pssCallback4",$html);
				unset($GLOBALS['_paatempbase']);
			}
			//return '';
			return $html;
		}

		function getStorm(){
			return $this->instance->storm;
		}

	}
	
	function _pssCallback3($a){
		$baseDir = $GLOBALS['_psstempbase'];
		if(!empty($a[4])){
			return $a[1] . '"' . $baseDir . '/' . $a[4] . '"';
		}
		elseif(!empty($a[3])){
			return $a[1] . '"' . $baseDir . '/' . $a[3] . '"';
		}
		elseif(isset($a[7]) && !empty($a[7])){
			return $a[5] . '"' . $baseDir . '/' . $a[7] . '"';
		}
		elseif(isset($a[8]) && !empty($a[8])){
			return $a[5] . '"' . $baseDir . '/' . $a[8] . '"';
		}

	}
	function _pssCallback4($a){
		return $a[1] . $GLOBALS['_psstempbase'] . '/' . $a[2] . $a[3]; 
	}
	
	class PSS{
		var $filePath;
		var $storm;
		var $rules = array();
		var $selectors = array();
		var $data = array();
		var $constant = null;
		
		function __construct($url){
			$this->filePath = $url;
		}

		function setSelector($selector){
			$this->selectors[] = $selector;
		}
		function setData($array){
			$this->data[] = $array;
		}
		function setRules($rules){
			$this->rules[] = $rules;
		}
		function setConstant($constant){
			$this->constant = $constant;
		}

		function setValue(&$target,$k,$v){
			if($k == 'selector'){
				return;
			}
			elseif($k == 'text'){
				$target->text($v);
			}
			elseif($k == 'html'){
				$target->html($v);
			}
			else{
				$target->attr($k,$v);
			}
		}

		/*
			这里将来可记录target的原则器，重新选择该元素
		*/
		function synchronous($target,$len){
			try{
				$parentNode = $target->parentNode();
				if($target->length > $len){
					for($i=$len;$i<$target->length;$i++){
						$target->eq($i)->remove();
					}
				}
				else{
					for($i=$target->length;$i<$len;$i++){
						$parentNode->append($target->cloneNode());
					}
				}
				
			}
			catch(Exception $e){
				
			}
		}

		function bindData($arr,$deep,$rule,$base,$childWord='children'){
			$targets = array();
			foreach($rule[$deep] as $val){
				if($val['selector'] === '.'){
					$target = $base;
				}else{
					$target = $base->find($val['selector']);
				}
				$this->synchronous($target,count($arr));
				if($val['selector'] === '.'){
					$targets[] = $base;
				}else{
					$targets[] = $base->find($val['selector']);
				}
			}
			foreach($arr as $key => $val){
				if(isset($val[$childWord])){
					$this->bindData($val[$childWord],$deep+1,$rule,$base,$childWord);
				}
				foreach($targets as $rkey => $target){
					$r = $rule[$deep][$rkey]['items'];
					$target = $target->eq($key);
					foreach($r as $k => $v){
						preg_match_all("/(\\$+)(\.[\w-]+)*/",$v,$arr);
						foreach($arr[0] as $preg_key => $preg_val){
							$temp = explode('.',$preg_val);
							$d = $val;
							for($i=1;$i<count($temp);$i++){
								$d = $d[$temp[$i]];
							}
							$v = str_replace($preg_val,$d,$v);
						}
						$this->setValue($target,$k,$v);
					}
				}
				
			}
		}

		function execute(){
			//$this->storm = $storm = RainStorm::loadFromFile($this->filePath);
			$this->storm = $storm = RainStorm::loadFromPHP($this->filePath);
			
			//处理所有constant
			if(!is_null($this->constant)){
				foreach($this->constant as $const){
					$target = $storm->find($const['selector']);
					foreach($const['items'] as $k => $v){
						$this->setValue($target,$k,$v);
					}
				}
			}
			//处理所有变量（需要从数据库读取的）
			foreach($this->selectors as $key => $selector){
				$target = $storm->find($selector);
				$data = $this->data[$key];
				//强制同步最外层的数量
				//至少会有一个元素，如果没有，那PSS就无法生效
				//$count = 
				if(is_array($data)){
					//如果设定了set规则
					if(count($this->rules[$key]['items'])){
						$_selector = $selector;
						$deep = array();
						foreach($this->rules[$key]['items'] as $rule){
							$_deep = 0;
							if(isset($rule['count'])){
								$_deep = strlen($rule['count']);
							}else{
								foreach($rule as $k => $v){
									preg_match_all("/(\\$+)(\.[\w-]+)*/",$v,$arr);
									if(count($arr[1])){
										$_deep = strlen($arr[1][0]);
										break;
									}
								}
							}
							
							if($_deep){
								$_deep--;
								if(!isset($deep[$_deep])){
									$deep[$_deep] = array();
								}
								$selector = $rule['selector'];
								unset($rule['selector']);
								unset($rule['count']);
								$deep[$_deep][] = array(
									'selector' => $selector,
									'items' => $rule
								);
							}
						}

						$this->synchronous($target,count($data));
						$target = $storm->find($_selector);
						$this->bindData($data,0,$deep,$target,isset($this->rules[$key]['define']['$']) ? $this->rules[$key]['define']['$'] : 'children');
					}
					else{
						$this->synchronous($target,count($data));
						$target = $storm->find($selector);
						foreach($data as $key => $val){
							if(is_array($val)){
								foreach($val as $k => $v){
									@$this->setValue($target->eq($key),$k,$v);
								}
							}
							elseif(is_int($key)){
								@$this->setValue($target->eq($key),'html',$val);
							}
							else{
								@$this->setValue($target,$key,$val);
							}
						}
					}
					
				}else{
					$this->synchronous($target,1);
					$target = $storm->find($selector);
					$this->setValue($target,'html',$data);
				}
				
			}
			//echo $storm->hurricanes[0]->current->saveHTML();
			return $storm;
		}

	}


?>